/* =========================================
 *           用户管理子服务实现 
 * =========================================*/
#pragma once
#include <brpc/server.h>
#include <memory>
#include <string>
#include <regex>
#include <sys/stat.h>
#include "dms.hpp"
#include "etcd.hpp"
#include "channel.hpp"
#include "base.pb.h"
#include "file.pb.h"
#include "user.pb.h"
#include "user.hxx"
#include "utils.hpp"
#include "data_cache.hpp"
#include "odb_factory.hpp"
#include "user_dao.hpp"
#include "search_repo.hpp"
#include "logger.hpp"

namespace user {

class UserServiceImpl;
class UserServer;
class UserServerBuilder;
using UserServiceImplPtr = std::shared_ptr<UserServiceImpl>;
using UserServerPtr = std::shared_ptr<UserServer>;
using UserServerBuilderPtr = std::shared_ptr<UserServerBuilder>;

class UserServiceImpl : public im_proto::UserService
{
public:
    UserServiceImpl(const shared::infra::DMSClientPtr& dmsClient,
        const shared::dal::ESClientPtr& esClient,
        const shared::dal::RedisPtr& redisClient,
        const shared::dal::OdbPtr& mysqlClient,
        const shared::infra::RpcServiceManagerPtr& rpcServiceMgr,
        const std::string& fileServiceName) 
        : dmsClient_(dmsClient)
        , esUser_(std::make_shared<shared::dal::UserSearch>(esClient))
        , sessionCache_(std::make_shared<shared::dal::Session>(redisClient))
        , statusCache_(std::make_shared<shared::dal::Status>(redisClient))
        , verifyCodeCache_(std::make_shared<shared::dal::VerifyCode>(redisClient))
        , userDao_(std::make_shared<shared::dal::UserDao>(mysqlClient))
        , rpcServiceMgr_(rpcServiceMgr)
        , fileServiceName_(fileServiceName) {}

    ~UserServiceImpl() {}

    // 用户注册
    void UserRegister(google::protobuf::RpcController* controller,
                       const ::im_proto::UserRegisterReq* request,
                       ::im_proto::UserRegisterRsp* response,
                       ::google::protobuf::Closure* done)
    {   
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());

        // 1. 获取用户名密码 && 校验
        std::string nickname = request->nickname();
        std::string passwd = request->password();
        if(!checkNickname(nickname)) {
            LOG_ERROR("{}-{}, 昵称长度不合法！", request->request_id(), nickname);
            return errResp(response, "昵称不合法");
        } 
        if(!checkPasswd(passwd)) {
            LOG_ERROR("{}-{}, 密码格式不合法！", request->request_id(), passwd);
            return errResp(response, "密码不合法");
        }
        // 2. 短信验证码校验
        std::string codeId = request->verify_code_id();
        std::string verifyCode = request->verify_code();
        auto verifyCodeCache = verifyCodeCache_->getVerifyCode(codeId);
        if(!verifyCodeCache || verifyCodeCache != verifyCode) {
            std::string codeCache = verifyCodeCache ? verifyCodeCache.value() : "null";
            LOG_ERROR("{}-{} 验证码({})错误", codeId, codeCache, codeId);
            return errResp(response, "验证码错误!");
        }
        verifyCodeCache_->remove(codeId);
        // 3. 用户数据库校验
        std::string phone = request->phone_number();
        auto user = userDao_->selectByPhone(phone);
        if(user) {
            LOG_ERROR("{}-{} 该手机号已被注册！", request->request_id(), phone);
            return errResp(response, "手机号已被注册!");
        }
        // 4. 用户数据持久化
        std::string uuid = shared::Uuid::createUuid();
        user = make_shared<entity::User>(uuid, passwd, nickname, phone);
        if(!userDao_->insert(user)) {
            LOG_ERROR("{}-{} [mysql]新增用户失败", request->request_id(), uuid);
            return errResp(response, "[mysql]新增用户失败");
        }
        // 5.向ES服务器中新增用户信息
        if(!esUser_->appendData(user->userId(), user->nickname(), user->phone(), "", "")) {
            LOG_ERROR("{}-{} [es]新增用户索引信息失败", request->request_id(), uuid);
            return errResp(response, "[es]新增用户索引信息失败");
        }
        response->set_success(true);
    }
    // 用户登录
    void AccountLogin(google::protobuf::RpcController* controller,
                       const ::im_proto::AccountLoginReq* request,
                        ::im_proto::AccountLoginRsp* response,
                       ::google::protobuf::Closure* done)
    {   
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());

        // 1. 获取昵称和密码 && 信息校验
        std::string nickname = request->nickname();
        std::string passwd = request->password();
        entity::UserPtr user = userDao_->selectByNickname(nickname);
        if(!user || user->password() != passwd) {
            LOG_ERROR("{}:{}-{} 用户名或密码错误!", request->request_id(), nickname, passwd);
            return errResp(response, "用户名或密码错误!");
        }
        // 3. 检测用户是否已经登录(TODO: 自动下线原登录设备, 在当前设备登录)
        if(statusCache_->exists(user->userId())) {
            LOG_ERROR("{}-{} 用户已在其它地方登录!", request->request_id(), user->userId());
            return errResp(response, "用户已在其它地方登录!");     
        }
        // 4. 新增登录状态缓存 && 返回响应
        if(!sessionCache_->append(shared::Uuid::createUuid(), user->userId())) {
            LOG_ERROR("{}-{} 新增用户登录状态缓存失败!", request->request_id(), user->userId());
            return errResp(response, "用户登录状态缓存登记失败!");
        }
        response->set_success(true);
    }   
    // 手机登录
    void PhoneLogin(google::protobuf::RpcController* controller,
                       const ::im_proto::PhoneLoginReq* request,
                       ::im_proto::PhoneLoginRsp* response,
                       ::google::protobuf::Closure* done)
    {   
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());

        // 1. 获取手机号 && 格式校验
        std::string phone = request->phone_number();
        if(!checkPhoneNumber(phone)) {
            LOG_ERROR("{}-{} 手机号码格式错误!", request->request_id(), phone);
            return errResp(response, "手机号码格式错误!");
        }
        // 2. 验证码缓存校验
        std::string codeId = request->verify_code_id();
        std::string verifyCode = request->verify_code();
        auto verifyCodeCache = verifyCodeCache_->getVerifyCode(codeId);
        if(verifyCodeCache != verifyCode) {
            LOG_ERROR("{}-{} 验证码({})错误", codeId, verifyCodeCache_->getVerifyCode(codeId).value(), codeId);
            return errResp(response, "验证码错误!");
        }
        verifyCodeCache_->remove(codeId);
        // 3. 数据库查询用户合法性
        entity::UserPtr user = userDao_->selectByPhone(phone);
        if(!user) {
            LOG_ERROR("{}-{} 该手机号暂未被注册!", request->request_id(), phone);
            return errResp(response, "该手机号暂未被注册!");
        }
        // 4. 检测用户是否已经登录(TODO: 自动下线原登录设备, 在当前设备登录)
        if(statusCache_->exists(user->userId())) {
            LOG_ERROR("{}-{} 用户已在其它地方登录!", request->request_id(), user->userId());
            return errResp(response, "用户已在其它地方登录!");     
        }
        // 5. 新增登录状态缓存 && 返回响应
        if(!sessionCache_->append(shared::Uuid::createUuid(), user->userId())) {
            LOG_ERROR("{}-{} 新增用户登录状态缓存失败!", request->request_id(), user->userId());
            return errResp(response, "用户登录状态缓存登记失败!");
        }
        response->set_success(true);
    }
    // 获取手机验证码
    void GetPhoneVerifyCode(google::protobuf::RpcController* controller,
                       const ::im_proto::PhoneVerifyCodeReq* request,
                       ::im_proto::PhoneVerifyCodeRsp* response,
                       ::google::protobuf::Closure* done)
    {   
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());

        // 1. 获取手机号码 && 格式校验
        std::string phone = request->phone_number();
        if(!checkPhoneNumber(phone)) {
            LOG_ERROR("{}-{} 手机号码格式错误!", request->request_id(), phone);
            return errResp(response, "手机号码格式错误!");
        }
        // 2. 生成6位随机验证码 && 调用平台sdk发送
        std::string codeId = shared::Uuid::createUuid();
        std::string verifyCode = shared::VerifyCode::generate();
        LOG_DEBUG("验证码: {}", verifyCode);
        if(!dmsClient_->send(phone, verifyCode)) {
            LOG_ERROR("{}-{} 短信验证码发送失败!", request->request_id(), phone);
            return errResp(response, "短信验证码发送失败!");
        }
        // 5. 构造codeID, 新增redis缓存
        if(!verifyCodeCache_->append(codeId, verifyCode)) {
            LOG_ERROR("{}-{} 新增验证码缓存失败!", codeId, verifyCode);
            return errResp(response, "验证码缓存登记失败!");
        }
        // 6. 组织响应，返回生成的验证码 ID
        response->set_success(true);
        response->set_verify_code_id(codeId);
    }
    // 获取用户信息
    void GetUserInfo(google::protobuf::RpcController* controller,
                       const ::im_proto::GetUserInfoReq* request,
                       ::im_proto::GetUserInfoRsp* response,
                       ::google::protobuf::Closure* done)
    {   
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());

        // 1. 获取userId, 数据库校验
        std::string userId = request->user_id();
        auto user = userDao_->selectByUserId(userId);
        if(!user) {
            LOG_ERROR("{}-{} 异常！数据库查询不到该用户!", request->request_id(), userId);
            return errResp(response, "数据库查询不到该用户!");
        }
        // 2. 构造响应
        im_proto::UserInfo* userInfo = response->mutable_user_info();
        userInfo->set_user_id(user->userId());
        userInfo->set_nickname(user->nickname());
        userInfo->set_description(user->description());
        userInfo->set_phone(user->phone());

        // 2. 调用文件子服务获取头像数据
        if(user->avatarId().size()) {
            // 2-1 获取rpc通信信道
            auto channel = rpcServiceMgr_->choose(fileServiceName_);
            if(!channel) {
                LOG_ERROR("{}-{}-{} 文件存储子服务节点不存在!", request->request_id(), userId, fileServiceName_);
                return errResp(response, "文件存储子服务节点不存在");
            }  
            // 2-2 构造请求体 && 通过信道发起服务请求
            im_proto::GetSingleFileReq req;
            req.set_request_id(request->request_id());
            req.set_file_id(user->avatarId());
    
            brpc::Controller ctl;
            im_proto::GetSingleFileRsp resp;
            im_proto::FileService_Stub stub(channel.get());
            stub.GetSingleFile(&ctl, &req, &resp, nullptr);  // 阻塞等待响应
            if(ctl.Failed() || !resp.success()) {
                LOG_ERROR("{}-{} 获取头像数据失败!", request->request_id(), ctl.ErrorText());
                return errResp(response, "获取头像数据失败");
            }
            userInfo->set_avatar(resp.file_data().file_content());   
        }
        response->set_success(true);
    }
    // 批量获取用户信息
    void GetMultiUserInfo(google::protobuf::RpcController* controller,
                       const ::im_proto::GetMultiUserInfoReq* request,
                       ::im_proto::GetMultiUserInfoRsp* response,
                       ::google::protobuf::Closure* done)
    {
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());

        // 1. 获取userId列表 && 数据库校验
        auto userIds = request->users_id();
        std::vector<entity::UserPtr> dbUsers = userDao_->selectMultiUsers(request->users_id());
        if(dbUsers.size() !=  userIds.size()) {
            LOG_ERROR("{}-{}, 用户条数({})与数据库不匹配!", request->request_id(), userIds.size(), dbUsers.size());
            return errResp(response, "用户条数与数据库不匹配");
        }

        // 2. 构造文件服务响应体
        auto channel = rpcServiceMgr_->choose(fileServiceName_);
        if(!channel) {
            LOG_ERROR("{}-{} 文件存储子服务节点不存在!", request->request_id(), fileServiceName_);
            return errResp(response, "文件存储子服务节点不存在");
        }  
        im_proto::GetMultiFileReq req;
        req.set_request_id(request->request_id());
        for(auto& user : dbUsers) {
            if(user->avatarId().empty()) continue;
            req.add_file_id_list(user->avatarId());
        }

        // 3. 文件服务请求
        brpc::Controller ctl;
        im_proto::GetMultiFileRsp resp;
        im_proto::FileService_Stub stub(channel.get());
        stub.GetMultiFile(&ctl, &req, &resp, nullptr);
        if(ctl.Failed() || !resp.success()) {
            LOG_ERROR("{}-{} 批量获取头像数据失败!", request->request_id(), ctl.ErrorText());
            return errResp(response, "批量获取头像数据失败");
        }
        
        // 4. 将文件服务的响应数据带回给用户响应
        auto avatarDataMap = resp.file_data();
        for(auto& user : dbUsers) {
            im_proto::UserInfo userInfo;
            userInfo.set_user_id(user->userId());
            userInfo.set_nickname(user->nickname());
            userInfo.set_phone(user->phone());
            userInfo.set_description(user->description());
            auto it = avatarDataMap.find(user->avatarId());
            if(it != avatarDataMap.end()) {
                userInfo.set_avatar(it->second.file_content());
            }

            auto usersMap = response->mutable_users_info();
            usersMap->emplace(user->userId(), userInfo);
        }
        response->set_success(true);
    }
    // 设置用户头像
    void SetUserAvatar(google::protobuf::RpcController* controller,
                       const ::im_proto::SetUserAvatarReq* request,
                       ::im_proto::SetUserAvatarRsp* response,
                       ::google::protobuf::Closure* done)
    {   
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());

        // 1. 获取用户信息 && 数据库校验
        const std::string& userId = request->user_id();
        const std::string& avatarData = request->avatar();
        auto user = userDao_->selectByUserId(userId);
        if(!user) {
            LOG_ERROR("{}-{} 异常！数据库查询不到该用户!", request->request_id(), userId);
            return errResp(response, "数据库查询不到该用户!");
        }
        // 2. 上传头像到文件子服务
        auto channel = rpcServiceMgr_->choose(fileServiceName_);
        if(!channel) {
            LOG_ERROR("{}-{}-{} 文件存储子服务节点不存在!", request->request_id(), userId, fileServiceName_);
            return errResp(response, "文件存储子服务节点不存在");
        }  
        // 2-2 构造请求体 && 通过信道发起服务请求
        im_proto::PutSingleFileReq req;
        req.set_request_id(request->request_id());
        auto fileData = req.mutable_file_data();
        fileData->set_file_name("");
        fileData->set_file_size(avatarData.size());
        fileData->set_file_content(avatarData);

        brpc::Controller ctl;
        im_proto::PutSingleFileRsp resp;
        im_proto::FileService_Stub stub(channel.get());
        stub.PutSingleFile(&ctl, &req, &resp, nullptr);
        if(ctl.Failed() || !resp.success()) {
            LOG_ERROR("{}-{} 上传头像失败!", request->request_id(), ctl.ErrorText());
            return errResp(response, "上传头像失败");
        } 
        // 3. 更新数据库
        const std::string& avatarId = resp.file_info().file_id();
        user->setAvatarId(avatarId);
        if(!userDao_->update(user)) {
            LOG_ERROR("{}-{} [mysql]更新用户头像失败", request->request_id(), avatarId);
            return errResp(response, "[mysql]更新用户头像失败");
        }
        // 4. 更新es索引
        if(!esUser_->appendData(userId, user->nickname(), user->phone(), user->description(), avatarId)) {
            LOG_ERROR("{}-{} [es]更新用户头像失败", request->request_id(), avatarId);
            return errResp(response, "[es]更新用户头像失败");
        }
        response->set_success(true);
    }
    // 设置用户昵称
    void SetUserNickname(google::protobuf::RpcController* controller,
                       const ::im_proto::SetUserNicknameReq* request,
                       ::im_proto::SetUserNicknameRsp* response,
                       ::google::protobuf::Closure* done)
    {   
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());

        // 1. 获取用户信息 && 数据库校验
        const std::string& userId = request->user_id();
        const std::string& newNickname = request->nickname();
        if(!checkNickname(newNickname)) {
            LOG_ERROR("{}-{}, 昵称长度不合法！", request->request_id(), newNickname);
            return errResp(response, "昵称不合法");
        }
        auto user = userDao_->selectByUserId(userId);
        if(!user) {
            LOG_ERROR("{}-{} 异常！数据库查询不到该用户!", request->request_id(), userId);
            return errResp(response, "数据库查询不到该用户!");
        }
        // 2. 更新数据库
        user->setNickname(newNickname);
        if(!userDao_->update(user)) {
            LOG_ERROR("{}-{} [mysql]更新用户昵称失败({})", request->request_id(), userId, newNickname);
            return errResp(response, "[mysql]更新用户昵称失败");
        }
        // 4. 更新es索引
        if(!esUser_->appendData(userId, newNickname, user->phone(), user->description(), user->avatarId())) {
            LOG_ERROR("{}-{} [es]更新用户昵称失败({})", request->request_id(), userId, newNickname);
            return errResp(response, "[es]更新用户昵称失败");
        }
        response->set_success(true);
    }
    // 设置用户签名
    void SetUserDescription(google::protobuf::RpcController* controller,
                       const ::im_proto::SetUserDescriptionReq* request,
                       ::im_proto::SetUserDescriptionRsp* response,
                       ::google::protobuf::Closure* done)
    {   
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());

        // 1. 获取用户信息 && 数据库校验
        const std::string& userId = request->user_id();
        const std::string& desc = request->description();
        if(!checkDescription(desc)) {
            LOG_ERROR("{}-{}, 签名长度不合法！", request->request_id(), desc);
            return errResp(response, "签名长度不合法");
        }
        auto user = userDao_->selectByUserId(userId);
        if(!user) {
            LOG_ERROR("{}-{} 异常！数据库查询不到该用户!", request->request_id(), userId);
            return errResp(response, "数据库查询不到该用户!");
        }
        // 2. 更新数据库
        user->setDscription(desc);
        if(!userDao_->update(user)) {
            LOG_ERROR("{}-{} [mysql]更新用户签名失败({})", request->request_id(), userId, desc);
            return errResp(response, "[mysql]更新用户签名失败");
        }
        // 4. 更新es索引
        if(!esUser_->appendData(userId, user->nickname(), user->phone(), desc, user->avatarId())) {
            LOG_ERROR("{}-{} [es]更新用户签名失败({})", request->request_id(), userId, desc);
            return errResp(response, "[es]更新用户签名失败");
        }
        response->set_success(true);

    }  
    // 设置用户手机号
    void SetUserPhoneNumber(google::protobuf::RpcController* controller,
                       const ::im_proto::SetUserPhoneNumberReq* request,
                       ::im_proto::SetUserPhoneNumberRsp* response,
                       ::google::protobuf::Closure* done)
    {   
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());

        // 1. 获取用户信息 && 验证码校验
        const std::string& userId = request->user_id();
        const std::string& phone = request->phone_number();
        const std::string& codeId = request->phone_verify_code_id();
        const std::string& code = request->phone_verify_code();
        auto codeCache = verifyCodeCache_->getVerifyCode(codeId);
        if(!checkPhoneNumber(phone)) {
            LOG_ERROR("{}-{} 手机号码格式错误!", request->request_id(), phone);
            return errResp(response, "手机号码格式错误!");
        }
        if(codeCache != code) {
            LOG_ERROR("{}-{} 验证码({})错误", codeId, verifyCodeCache_->getVerifyCode(codeId).value(), codeId);
            return errResp(response, "验证码错误!");
        }
        // 2. 更新数据库
        auto user = userDao_->selectByUserId(userId);
        if(!user) {
            LOG_ERROR("{}-{} 异常！数据库查询不到该用户!", request->request_id(), userId);
            return errResp(response, "数据库查询不到该用户!");
        }
        user->setPhone(phone);
        if(!userDao_->update(user)) {
            LOG_ERROR("{}-{} [mysql]更新用户手机号失败({})", request->request_id(), userId, phone);
            return errResp(response, "[mysql]更新用户手机号失败");
        }
        // 4. 更新es索引
        if(!esUser_->appendData(userId, user->nickname(), phone, user->description(), user->avatarId())) {
            LOG_ERROR("{}-{} [es]更新用户手机号失败({})", request->request_id(), userId, phone);
            return errResp(response, "[es]更新用户手机号失败");
        }
        response->set_success(true);
    }
private:
    bool checkNickname(const std::string& nickname) { return nickname.size() < 31; }
    bool checkDescription(const std::string& desc) { return desc.size() < 91; }
    bool checkPasswd(const std::string& passwd)
    {
        // ^ 表示字符串开始，$ 表示字符串结束
        // [a-zA-Z0-9_@.+-*/] 匹配字母、数字、符号
        // {6,20} 表示前面的字符类出现6到20次
        std::regex pattern("^[-a-zA-Z0-9_@#.+*/]{6,20}$");
        return std::regex_match(passwd, pattern);
    }
    bool checkPhoneNumber(const std::string& phone)
    {
        std::regex pattern("^1[3-9][0-9]{9}$");
        return std::regex_match(phone, pattern);
    }
    template<class Resp>
    void errResp(Resp* response, const std::string& errmsg) {
        response->set_success(false);
        response->set_errmsg(errmsg);
    }
private:
    shared::infra::DMSClientPtr dmsClient_; // 短信验证码平台

    shared::dal::UserSearchPtr esUser_;     // 用户搜索引擎对象

    shared::dal::SessionPtr sessionCache_;       // 用户会话的临时数据
    shared::dal::StatusPtr statusCache_;         // 用户状态的临时数据
    shared::dal::VerifyCodePtr verifyCodeCache_; // 用户短信验证码的临时数据

    shared::dal::userDaoPtr userDao_;       // 持久化数据的操作对象

    shared::infra::RpcServiceManagerPtr rpcServiceMgr_;     // rpc的服务管理对象

    std::string fileServiceName_;   // 文件子服务名称, 通过rpc获取用户头像数据
};

class UserServer
{
public:
    UserServer(const shared::dal::ESClientPtr& esClient,
        const shared::dal::RedisPtr& redisClient,
        const shared::dal::OdbPtr& mysqlClient,
        const shared::infra::DiscoveryPtr& rpcSvrDisc,
        const shared::infra::RegistrantPtr& rpcSvrRegist, 
        const std::shared_ptr<brpc::Server>& rpcServer)
        : esClient_(esClient), redisClient_(redisClient), mysqlClient_(mysqlClient)
        , rpcSvrDisc_(rpcSvrDisc), rpcSvrRegist_(rpcSvrRegist), rpcServer_(rpcServer) {}
    ~UserServer() {}
    void start() { rpcServer_->RunUntilAskedToQuit(); }

private:
    shared::dal::ESClientPtr esClient_;     // 支撑用户业务功能的搜索引擎句柄
    shared::dal::RedisPtr redisClient_;     // 用户的临时数据缓存句柄
    shared::dal::OdbPtr mysqlClient_;       // 用户数据的持久化句柄

    // 这三个对象都不是直接面向业务功能的, 而是为业务功能提供支撑
    // 因此服务没有结束, 这三个对象就不能释放, 于是放在UserServer中
    std::shared_ptr<brpc::Server> rpcServer_;        // rpc服务器
    shared::infra::DiscoveryPtr rpcSvrDisc_;         // rpc服务发现对象
    shared::infra::RegistrantPtr rpcSvrRegist_;      // rpc服务注册对象
};

class UserServerBuilder
{
public:
    // 构造短信平台发送对象
    void makeDmsObject(const std::string& accessKeyId, const std::string& accessKeySecret) 
    {
        dmsClient_ = std::make_shared<shared::infra::DMSClient>(accessKeyId, accessKeySecret);
    }
    // 构造搜索引擎对象
    void makeESObject(const std::vector<std::string>& hostList)
    {
        esClient_ = shared::dal::ESClientFactory::create(hostList);
    }
    // 构造redis客户端对象
    void makeRedisObject(const std::string& host, uint16_t port, int db, bool keepAlive)
    {
        redisClient_ = shared::dal::RedisClientFactory::create(host, port, db, keepAlive);
    }
    // 构造odb-mysql客户端对象
    void makeMysqlObject(const std::string& user,  const std::string& passwd, 
        const std::string& host, const std::string& db,
        const std::string& cset, uint16_t port, uint16_t connectPoolCount)
    {
        mysqlClient_ = shared::dal::ODBFactory::create(
            user, passwd, host, db, cset, port, connectPoolCount
        );
    }
    // 构造Rpc服务注册客户端对象
    void makeRpcSvrRegistrant(const std::string& host, const std::string& serviceName, const std::string& accessHost)
    {
        rpcSvrRegist_ = std::make_shared<shared::infra::Registrant>(host);
        rpcSvrRegist_->registry(serviceName, accessHost);
    }
    // 构造Rpc服务发现客户端对象
    void makeRpcSvrDiscoverer(const std::string& host, const std::string& baseServiceName, const std::string& fileServiceName)
    {
        // 用户管理子服务中需要关注文件子服务, 用于上传下载用户头像数据
        fileServiceName_ = fileServiceName;     
        rpcServiceMgr_ = std::make_shared<shared::infra::RpcServiceManager>();
        rpcServiceMgr_->declared(fileServiceName);

        auto putCb = std::bind(
            &shared::infra::RpcServiceManager::onServiceOnline, rpcServiceMgr_.get(),
            std::placeholders::_1, std::placeholders::_2
        );
        auto delCb = std::bind(
            &shared::infra::RpcServiceManager::onServiceOffline, rpcServiceMgr_.get(),
            std::placeholders::_1, std::placeholders::_2
        );
        rpcSvrDisc_ = std::make_shared<shared::infra::Discovery>(host, baseServiceName, putCb, delCb);
    }
    // 构造RPC服务器对象
    void makeRpcServer(uint16_t port, int32_t timeout, uint8_t numThreads)
    {
        if (!esClient_) {
            LOG_ERROR("还未初始化ES搜索引擎模块!");
            abort();
        }
        if (!redisClient_) {
            LOG_ERROR("还未初始化redis缓存模块!");
            abort();
        }
        if (!mysqlClient_) {
            LOG_ERROR("还未初始化mysql持久化模块!");
            abort();
        }
        if (!rpcServiceMgr_) {
            LOG_ERROR("还未初始化rpc服务管理模块!");
            abort();
        }
        if (!dmsClient_) {
            LOG_ERROR("还未初始化短信平台模块！");
            abort();
        }
        rpcServer_ = std::make_shared<brpc::Server>();
        
        // 向rpc服务器添加服务实现类
        UserServiceImpl* userServiceImpl = new UserServiceImpl(
            dmsClient_, esClient_, redisClient_, mysqlClient_, rpcServiceMgr_, fileServiceName_
        );
        if(rpcServer_->AddService(userServiceImpl, brpc::ServiceOwnership::SERVER_OWNS_SERVICE) == -1) {
            LOG_ERROR("添加Rpc服务失败!");
            abort();
        }
        // 启动服务器
        brpc::ServerOptions options;    
        options.idle_timeout_sec = timeout;  
        options.num_threads = numThreads;        
        if(rpcServer_->Start(port, &options) == -1) {
            LOG_ERROR("Rpc服务启动失败!");
            abort();
        }
    }
    UserServerPtr build()
    {
        if(!rpcSvrDisc_) {
            LOG_ERROR("还未初始化服务发现模块!");
            abort();
        }
        if (!rpcSvrRegist_) {
            LOG_ERROR("还未初始化服务注册模块!");
            abort();
        }
        if (!rpcServer_) {
            LOG_ERROR("还未初始化RPC服务器模块!");
            abort();
        }
        return std::make_shared<UserServer>(
            esClient_, redisClient_, mysqlClient_, rpcSvrDisc_, rpcSvrRegist_, rpcServer_
        );
    }
private:
    shared::infra::DMSClientPtr dmsClient_; // 短信验证码平台

    shared::dal::ESClientPtr esClient_;     // 支撑用户业务功能的搜索引擎句柄
    shared::dal::RedisPtr redisClient_;     // 用户的临时数据缓存句柄
    shared::dal::OdbPtr mysqlClient_;       // 用户数据的持久化句柄

    std::shared_ptr<brpc::Server> rpcServer_;               // rpc服务器
    shared::infra::RpcServiceManagerPtr rpcServiceMgr_;     // rpc的服务管理对象
    shared::infra::RegistrantPtr rpcSvrRegist_;             // rpc服务注册对象
    shared::infra::DiscoveryPtr rpcSvrDisc_;                // rpc服务发现对象
    
    std::string fileServiceName_;   // 文件子服务名称, 通过rpc获取用户头像数据
};


}